{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "![](../images/wait_for_user_input.png)"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "2e00ce4004b18c8e"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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KGvD91xaQS0loBb0OGoOoJCy7sPIY6bse8eiiY2KKOrubGMYNGtAuNSAAB0BaFiVHmNNPUHJ7tY7lCWjkW2m1zUjmu14lxkqJd4aeIAe2uzEMPtGBY/BZLFSdo2yB8skcHKvk3XSSOled55Ljq97j08NdBoNAsrabiZUXZPyk1nvTF/WkUoouyflJrPemL+tItn7K/L7wseK7oiLykEREBERAREQEREBVLaj6kf3jb/nsCtqjcjsrchs1RQOldAZN1zJWjUxva4PY7Tx6OaDp49FvwK4oxaK6tUTE+qxolHIoZ1bkdIBHNi9RWSt4OloKqn5J3sjlJGOAPkIX57rX7zMuvWqL69ehmf2j5o6lk2ihO61+8zLr1qi+vTutfvMy69aovr0zP7R80dVsm0VTuOb19qulqt1Vil1irLpK+Gkj5ekPKPZG6Rw1E2g0Yxx46dGnSpHutfvMy69aovr0zP7R80dSybRQnda/eZl161RfXp3Wv3mZdetUX16Zn9o+aOpZNqLsn5Saz3pi/rSL0i6348OZtzHsuqqPT/hOpnF7HVwV1Xd7kyOGuqo2QMponl7YImFxALtBq8l5LiBoPBaNd3edjXbDoqvMaYtomJ8eBqWREReUxEREBERAREQEREBERAREQEREGf56NdpOzLhrpcK3xa6feE/sHT/AIf6HQFnuft12l7LzoTpca06ga6feE/T5FoSAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgzzaAR3zNl2p490a3Thr/h9R/BaGs+z4O75WzDQvA7o1u9ujhp2hP0+x/rotBQEREBERAREQEREBERARFC3jNsex+qFNc75brfUkb3I1NUxj9PLuk66LOmiqubUxeVtdNIqt30sO86bR12P0p30sO86bR12P0rb2fG3J5SubOxaUVW76WHedNo67H6U76WHedNo67H6U7Pjbk8pM2di0oqt30sO86bR12P0p30sO86bR12P0p2fG3J5SZs7FpRVbvpYd502jrsfpTvpYd502jrsfpTs+NuTykzZ2M92j7VMIpNqOz+KozCwQzW651zKuOS5wNdTOFFOwiQF43DvHd0Pj4dK2Ogr6a60NNW0VTFWUdTG2aCop3h8csbgC17XDg5pBBBHAgr+bnZadjtYtonZKY3esbvVsFhyiZovlTBUxllDJHpykr9DoA9g4ani8Hyh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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from IPython.display import Image, display\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "    user_feedback: str\n",
    "\n",
    "\n",
    "def step_1(state):\n",
    "    print(\"---Step 1---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def human_feedback(state):\n",
    "    print(\"---human_feedback---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_3(state):\n",
    "    print(\"---Step 3---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "builder = StateGraph(State)\n",
    "builder.add_node(\"step_1\", step_1)\n",
    "builder.add_node(\"human_feedback\", human_feedback)\n",
    "builder.add_node(\"step_3\", step_3)\n",
    "builder.add_edge(START, \"step_1\")\n",
    "builder.add_edge(\"step_1\", \"human_feedback\")\n",
    "builder.add_edge(\"human_feedback\", \"step_3\")\n",
    "builder.add_edge(\"step_3\", END)\n",
    "\n",
    "# Set up memory\n",
    "memory = MemorySaver()\n",
    "\n",
    "# Add\n",
    "graph = builder.compile(checkpointer=memory, interrupt_before=[\"human_feedback\"])\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:58:44.191844Z",
     "start_time": "2024-11-05T01:58:42.100659Z"
    }
   },
   "id": "1d6c5c26fd5333c1",
   "execution_count": 1
  },
  {
   "cell_type": "markdown",
   "source": [
    "运行到human_feedback节点时，会触发中断，等待用户输入"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "5895935a64e61e5f"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"input\": \"hello world\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:59:37.316861Z",
     "start_time": "2024-11-05T01:59:37.296548Z"
    }
   },
   "id": "e862fd8237cb7aea",
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "source": [
    "现在可以输入，并更新graph 的状态"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "94fbb9ed98a21c6c"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--State after update--\n",
      "StateSnapshot(values={'input': 'hello world', 'user_feedback': 'yes'}, next=('step_3',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b19d-6a96-616e-8002-025fb4efaac4'}}, metadata={'source': 'update', 'step': 2, 'writes': {'human_feedback': {'user_feedback': 'yes'}}, 'parents': {}}, created_at='2024-11-05T02:01:14.968675+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b199-c745-6148-8001-bc234cc8bb91'}}, tasks=(PregelTask(id='615bd312-0056-3adf-fb53-fcea6021f76b', name='step_3', path=('__pregel_pull', 'step_3'), error=None, interrupts=(), state=None, result=None),))\n"
     ]
    },
    {
     "data": {
      "text/plain": "('step_3',)"
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Get user input\n",
    "try:\n",
    "    user_input = input(\"Tell me how you want to update the state: \")\n",
    "except:\n",
    "    user_input = \"go to step 3!\"\n",
    "\n",
    "# We now update the state as if we are the human_feedback node\n",
    "graph.update_state(thread, {\"user_feedback\": user_input}, as_node=\"human_feedback\")\n",
    "\n",
    "# We can check the state\n",
    "print(\"--State after update--\")\n",
    "print(graph.get_state(thread))\n",
    "\n",
    "# We can check the next node, showing that it is node 3 (which follows human_feedback)\n",
    "graph.get_state(thread).next"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:01:14.982905Z",
     "start_time": "2024-11-05T02:00:49.601181Z"
    }
   },
   "id": "fa7bbe4bba96b265",
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can proceed after our breakpoint -\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "b56a668a20a6a864"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world', 'user_feedback': 'yes'}\n",
      "---Step 3---\n"
     ]
    }
   ],
   "source": [
    "# Continue the graph execution\n",
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:02:38.555010Z",
     "start_time": "2024-11-05T02:02:38.546384Z"
    }
   },
   "id": "19f826e67a5438b8",
   "execution_count": 4
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can see our feedback was added to state -\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "b3a94a9326283ff6"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'input': 'hello world', 'user_feedback': 'yes'}"
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(thread).values\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:02:58.612758Z",
     "start_time": "2024-11-05T02:02:58.605100Z"
    }
   },
   "id": "60f9c23558fde2e5",
   "execution_count": 5
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Interacting with the Agent\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "c25285e259f43091"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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7aDwOtHzbNJLfPjeNnbHpvtsQUEf7mjjDm+MugY8vnNN3cLa7Sb7reyFlSyBYmYGwhs0j92Nja13OG+d3cw6rrq4LwUyN/gToh2mOIVWhpXC4q3bZT1RfzFWOnke1tSzRtY1zw9j+R53a8D3h23V7Hug+FrgSOJWkCANztnqvT/9iC/rWyWOr5fH2aNuMTVbMbopYz980jYhVvTnFzQuscm3G4DWmns5kXNL21MblYLEpaO8hjHk7D0nZWzuUxMxOMCC0PkZ8npmq+3J21yB8tOxIPv5YZHQvd/O6Mn+dTyrPDphdpdlrZwbftWr8fM3lPZzWJJI+n8h7VZl7X4iLtURvlM7REReCBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBVSjPHoTlx1siHBcxFG315KzfwEp7mAHcMcdm7bMOzg3nta/MkbJo3Me0PY4FrmuG4IPeCF6UV6uNM5xKYljkqV7Dg+SGKU7dHOaD0X48W1PisHzY+hQJ4fY6A/sbZyGFZ0+04+29kI27uWI7saP5LR/cF+fIif1pz3z8X6pempanZXxjliYRvWWGvFXBEUbIwepDGgbrIqt5ET+tOe+fi/VJ5ET+tOe+fi/VJ0dv5/SU4RvWlFyu1jsrFxWxunm6pzPi6xhbV95M0XadrHPXY3Y9n73llfv079uqtfkRP60575+L9UnR2/n9JMI3rNLDHO3lljbI3ffZ4BCw+LafxWD5sfQq/wCRE/rTnvn4v1SeRE/rTnvn4v1SdHb+f0kwjescVKvA/mjgijd/vNYAVW8nebrMT4fGSiXHu3iyGQif5rWdQ6GNw75D3Eg+YNyfO2B/Z4f0bLv2RvZTLM337G5df2R/lRs5WuHyOBCsdevFUgjggiZDDG0NZHG0Na0DuAA7gkTRbzpnGfT/AH6fyZQ+xRMhjZHGxscbAGtY0bBoHcAF+0RZ1RERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERBz28R/tAYQbnm8mL+w9G3hdP5f8A2/69CXPr2/8AtAYTu28mL/oG/wD9XT/n/wDb+5dBQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREHPLw/+YLCHmG/kvf8AN26n7rproa55f2/2g8H1O/kvf6bf+bp+ldDQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERARVG9q3J3Lc8WCoVbEFeR0Mlu9O6NrpGkhzWNaxxcGkbFxIG4IG+xWt481h8RwftU36ta40a5MYzhH8wnBd0VI8eaw+I4P2qb9WnjzWHxHB+1Tfq1PVa98cYMHjbUHu98tivdEx4qbhVYdqSgyxpoYyPMtcZZ5bEJa5r/B9+UmIbdOocD6F/QBeacp7n6bLe6Gx3F2ahhvHVSn2HgnbymKScDlZYcTHvztYS0fxNPeOvX/AB5rD4jg/apv1adVr3xxgwXdFSPHmsPiOD9qm/Vp481h8RwftU36tOq1744wYLuip0Gr8rjJI3Z2hThove2N1ujYfJ2LnHlaXscwbM3IBcCdt9yA0FwuK8Llqq3/AOjAREXkgREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREHO9BHfTURPebFkn5T28isKr2gf3sQ/8ez/nyKwrs3u1q85TO2REReKBERARaMmcx8WagxD7sDcpPA+1HTMg7V8THNa6QN7+UF7QT3buC3kFc4jnl0BqJw7xQmI/jDCujLnHEn+D7Uf5vn/wFdHVNI7Kjzq9qVu4REXPVEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERBzvQP72If8Aj2f8+RWFV7QP72If+PZ/z5FYV2b3a1ecpnbLyHxD1lqGHVN/W+lLmpGYbFasq4a1PkdQEUpneFx1rEEOOEZa6Pdzm9o5zXhwLhuAtXXOZ1DqLV+rcaNRarg4gV9V1auJ07jbFiGhJiDJARI8RbM5HQmZ75S4Oa4bbjuPfM57nDh1qPI5K9kNOCefIzG1Ya25YZGZztvOyNsgZHN0/bWBr+/zupXNdf8AuddX6i1zmclgLGI00zIXGWY87RzWVhuVyAwOeabZPBpZCG7bnlB6bg+nHMShXZncUeL+puId3T96ajPhM5aw2MMeqpcfDR7ANEb5aTakjJw/cSEyOPMH8o5QFLZOhqTVeqeL0eV1bncXc07hcdaq18Hk5a9aC46i98j2gbFzeeMeY7zTuSWknddf1LwC0Fq7UsufymAbNlZxGLE0NqeBtrk9520cb2sl22AHO13QAdysPkBgfGOpL3gH3VqKGOvlJO2k+6I443RsG3Ns3Zr3Ddux69eqtqyPO2ncWOJXGrhRqDLZLLV7+T4eDKTeL8pPVY6btaby0Nje0chMji5nvXbN5geUbeqVQ81wN0TqDE6bx13DONfTkAq4t0FyxDNWhDGs7MSska9zS1jQQ5xB5RvuVfFamMBW+JP8H2o/zfP/AICujrnHEo7cPtSHbf8AY+fp/wAhVmra7xMjq8dqSbE2ZaLsiYMlC6B0ULTs8vc4crS374b7gde4gqNI7Gjzq9qVu5YUWOvYitwRzwSsmglaHskjcHNe0jcEEdCCPSsi56oiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiLFZsw04HzWJWQQsG7pJHBrWj5Se5BlWK1aho1ZrNmaOvXhYZJZpXBrGNA3LnE9AABuSVDN1DayVpkeKx75oIbz6lyxdD6zY2sbu58QczebziGgjZpIcebzdj8xmlGxvx1zMWnZvM0mzNjvSsEbWiV27g2JvmN2bswEgu5RsXElxcFU4bW4b+jadmu8SQTSTyRvA980zPIP9BVnURJisvpeWeChi3ZnGyTSTw9hYjZNCXuc9zHNkLWloc48rg7uOxaOXd2Pxtn/AFNyftVP9eu1XhcqmumYwnPbEe8rTGM4ptFCeNs/6m5P2qn+vVRxvHCjmOI2Q0JRw121qrH1PDbdCKxVcYY+ZrfOf23IHbvb5u/Nsd9tlTo/3R9VPMwdJRVh+rMuzMRYw6NzHhcsD7LQJapZyNc1p3f23KDu9uzSdz1IGwO2542z/qbk/aqf69Oj/dH1U8zBNooTxtn/AFNyftVP9enjbP8Aqbk/aqf69Oj/AHR9VPMwa3EokcPdSbDc+L59h/yFeUMl/wBpRqGPXjdFWOEkcGaffbi5cfkM0IvtrniPkc90Qa0Enbmd5oB3PReuJcXmdWw+L7eJkwuPl2FqSzPE+R0e/VjBG5w3d0G5PQE9Cei1s57nvQupOLNHiNkMLHY1LUoy0Od37TOyRhj5pY+6RwjdJGC7ccryCDysLc+kTGpTRjnEzOWe3DkTswWexoXDSyyz16zsbadjjim2MfI6u+Kvvu1rOQgN5T1aQN27nbbcr8S4XPUWTHG5xtjkoMr16+VriRvbt/758jOV55h0cPh6jbuNfbwnk0se00JmptLsaDthpmG3iT8AFdzgYQPggfG34WlPsoX9K7R660/NhIgOubxrnXsWfle8NEkHTqTLG1g32EjlgVWC1n8ziWXJLen5LlevVjma/FTtlknkPSSNsT+Qjl7wdzzD4D0WV+ucHXmuxW77Mc6n4OJzkGurMaZ/2oB8gDXFx83ZpPneaevRSmMylLN4+C/jrkF+jO3nhs1ZWyRSN+Frmkgj5QstmrDdgfBYhjnheNnRytDmu/jB70GVFXL+gcTabk3VRYw1rJTR2LNvFTurTSSM964lp2J2Gx3BDh0O6/drHairS2paGYr2RNbikZXyNUFsEAG0kTHRlp3Pe1zg7Y7g7g9AsCKuy6jyuP7Z17TtiSPxg2rA7GTNsl0Dve2JGu5CwA9HNAcR6OYbkbFfWeFsTSxHIRV5WXzjOS2DA59nl5uzYHgc5LdyOXcOAJG4QTSIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgL45wa0ucQABuSfQoe1qeBltlWhDJmLDbjKVqOi9jvAi5naF027hyAMLXbe+POzYHmC16unbWRfSt5+02zcrOsctai6SGoWSea1skZce2LWebu/oS5zg1u4DQxS6pnzVd7dMQRZEzUhaqZaV4ONcXP5WjtGEuedg5+zAQQ0bubztJ2TpKtdtWJ8tK7M9rJXmZVtsa6tWkiHmuhj2808+793FzubbztmtDZuONsTGsY0MY0ANa0bAD4Av0gIiICIiCC1xiczntJ5PHafzbNOZizF2dfKvqeFCsSRu8Rc7OY7b7ecNiQeu2x8G+4v9znxP4c8fKutciK+d0vk4sjVu52tea7nc2WRge5knLK7nkha4ODSC2RpJHUD3FxX1HZ0toDLXKDuXKzMZRx3Tfe5O9sFfp6ftsjN/k3UxpTTlXR+mMRgqXN4HjakVOIu98WxsDQT8JO25PwoNGxK8cQ6MQlyYjOKsOMTI/uEkTQ7F7/AESjc8o9LTIfQrGq7bn7PiFiYe3yQ7XF3HiGNm9E8ktYc0jvRKOfZg9LTL/uqxICIiAiIgIiIKLk+EGIN+fKads29GZqZxkluYN7Y47D/S6eu4OhmJ6DmewvA965u61/KrWOjnhmo8CNR40bA5nTMZdKz4XS0XEyAf8ABdMTv7xo6roSIIXS2tMFrai+3gspWycMbuzlED93wv8ASyRh86Nw9LXAEfAppVXVPDLT+rbrcjZqPpZqNnJFmcZM6pejb3homjIcW77bscSw9xaR0UO0a+0SNiYuIWKZuSftVLLNbt0G3m153fMDr6UHQlhtUq91sYsQRWBHI2VglYHcr2ndrhv3EHqD6FXtMcR8Fqu5LQq2n1czCxsk+IyETq12FpHQuheA4t79njdp2OxKs6CuxaFxtJ8Jxr7WIay+7IyR0Z3Mjnkf78SMO7S13eW7d/UbHqvtalqXHPqsORp5eE3JHWJLUJglZXPvGs5N2ue09NyAHD4CNzYUQV6jqi3z42DKYG/j7d2aaLaFotQxcm5a58ke4Y14G7S4Dr0Ox2B3cDqfE6ox9a9ishXv1bIeYpIXg8/I7kf0792u80j0HoeqlFoXsDjMnfpXrePq2b1HtPBLUsLXS1+dvK/s3kbs5m9DykbjoUG+irlLR8mFZj4cTmslWp0oJYRTtzeGRzF3VjpJJuaYlh97tIBt0O4DeX5Bf1LjI67MhjqmXEdKSSzbxchhfJYb3Rx15CQA8dxMx2PQ9POQWRFAVNb4qeeGtZkkxl6TH+M3Vb8ZifFBvs4vJ80Fp6OHMdtwT0IJnmua9oc0hzSNwQdwQg+oiICIiAiIgIiICIiAiIgIiICIiAtPxtT/AA7VuLmeo9R4vSOEuZjM3oMbi6jO0ntWH8rGN326n5SQAO8kgDqg6B42p/GGJ42p/GGLimM45aIyunsvnI814Ni8S1j7s9+pPU7IP35Dyysa53MRs3lB3PQblfilx50Jf0/mM0zPCGhhzEMgbVWevLVEhAjc+KRjZGtcT0dy7HYnfYFB27xtT+MMTxtT+MMXDouO2kMhitQW8bemvT4Wkb89M0rEUz4djyvjY6MOkY4tID2BzflVS/2k6OU4GVda1XswmQsR1Y9svi776cFmVrXlpdHCHSR7cwErByE8vndQEHp7xtT+MMTxtT+MMXENSce9CaSzGTxWVzhr3sW6Nt+NlOxKKgfG2Rj5XMjLWMLXtPO4hu+433BAs1PWWIyOo7mBqXBYy1SrBdmgYx2zYZi8RvD9uUgmN/cSend1G4dHkzVKJjnunGzQTs0En+YAbn+ZV6xZl1VBNDZtSYfEWIIHsbWmfDfD+bnka97T9rGwDPMJd1eQ5pAK5lZ46aGqabwmfkzhloZpr5Mb2NKeWewwe+LIGRmXZvTcluw3G+24Vs0tqrEa1wdbMYO/FksbYB7OxCehIJDgQeoIIIIIBBBBCDpVepBTa9sEMcDXvdI4RtDeZ7ju5x27yT1J9KzIiAiIgIiICItDPZ2jpjCXstk7DauPpQusTzOBPKxo3J2HUn5B1PcEFP1Rzan4o6ZwTWF9PDRvz952x5Q8h8FRh69eZxnkHy1h8iv6pXC7BXqmNv57NQGvqDUVjxhbged3VGcoZXq/B9qiaxrtuhk7V49+rqgruam8G1hptxsZJrZxarCvWZzVXuLBIHTn70tETgw/C9w9KsSrutZHU6mNvibJsZTyNd8kOMj7R0zXu7EtkZ6Ym9rzu26gR8w35dlYkBERAREQEREBERAREQQup9G4TWdWGDNYyDINgf2sD5G7SQSeh8Ug2dG7/wATSD8qq/ijWWhG82JuP1rho2/uZlJWsyTBv3RWjsyXYdA2YAnbrMuhIgr2lNd4nWHhEVOSWvkau3heMvQur26xO4HaRPAcGkg8rxux4G7XOHVWFV/VOh8Xqx9axYbLUylPc08rSf2VusSQSGSbe9Ow5mO3Y/bZzXDotDTupMjjspFp3U/ZeNJA40clBGWV8kxrQXbDqIpgNyYiTu1pewkB4jC3oiICIiDFaqw3a01exDHYrzMMckUrQ5j2kbFrgehBHQgqBn0NRjbK7FWLmn7BoNxsEmMl5Y6sTDvGY67w6AOZ3AmM9PNO7eisaIK3bfqfEQ35oWU9QMjgi8EqD7ksSSDpLzyEuYeb3zdmsAPmnp5w/dvXWKxL8l43kkwdeh4P213JsMFU9sQ1gZO77W885DCA7cOIBHnN3sK/MkbZWOY9oexwIc1w3BHwFB9719VeuaJpSSWZ8fNZwdu1bju2LONeGOnkYOXz2uDmuDmjldu3qAPSAR9dPqPGzPL4KubgmyLWRisPBZKtR3e5/O5wlcw9/LyczT0buNnBYEUPjdWY3JSxQds6ncllmhjp3o3V55XRHaQsY8AvaNweZoLSHAgkEFTCDBYuQ1SBLIGE926xeNqfxhii9T/tsH8krlGoOOuhdLZ+XDZPPx1rsD2R2HdhK+Cs9+3K2aZrDHETuDs9zTsQfSg7X42p/GGJ42p/GGLiGo+PmhNJ5jJ4vKZwwXsY6Nt6NlOxKKgfG2Rj5XMjLWMLXtPO4hveN9wQN7WfGHR+gLFCvm8yyvZvRmavBXglsyPiHfLyxNcRGN/fkBvyoOxNylR7g0TtJJ2AW2uH8BNd3uJvDLS2qMlFWhu5KPtZGU2ubECJHNHKHOcdtgO8ldwQEREBERAXnL3RemsrqHReLsYnHSZqTDZyhmLGJiI570EEwfJE0OIBdt5wB7y0BejVUvEl38Af6w+lB534mZ2/xZ0bUu4bSGpWjTmdxmZmx2VxrqcuRiim5pYoWSEF7mtHNsQASGgFyofFvEZ/iseIGqMRpTO0sc7TlLC161/HSQ3MhOL4nc5lcjtOWNh23IG/M7bcDdexPEl38Af6w+lPEl38Af6w+lBxHVWmMnlePctivSn8CsaGu4/w4xO7ATutRFkbpNtubbmIbvvtuVzyw3M6g9x7No9mlNQ1NRYTF4zHT0rOMlaZ5YpYmvMBAImaBEXczNxsQV6x8SXfwB/rD6U8SXfwB/rD6UHnXLaZyktz3R58UXZGZelDHQ2rPIunxS2MiLp9s2fu3zd+vTvUfop2Y4Z60pZXI6Zz+Shy2jMRSiOOoPmcy3X7XtIJtv2l321uzpOVvfu4bFelG4m5JaezweQGJo6no07/AAHuO23o7t1m8SXfwB/rD6UHh3RXD7M6WxvDTO6iwGtDiGaUOHs1tNSXK+Qx9oWnygywwOZKWPa4A9DsWNJA6Fen+EGnMTp3RzXYfF5fDw5CzLfmrZ2aSW72r3ec+UyPe7mdsHbF2/Xrsd10PxJd/AH+sPpTxJd/AH+sPpQW1ERAREQEREBc9tPPErWgpxPcdMabtB1xw95fyDdnRwg/fRwEh7/QZezbvvFI1b2utQX571bSenZ+x1BkYjLLda0PGLqb8rrLgQRzk7tiY4HnfudnMjk5bHp7T9HSuEp4nGw9hRqRiONhcXuPwuc4klzidy5ziS4kkkkkoJFERBq5WgMrjLlJ089VtmF8JnqyGOaPmaRzMeOrXDfcEdx2K09MXJreHjFiC7XnrvfVf4wDRLKY3Fnaks80h4aHgjbo4dGndollXcrWbp/LPz1eCBsVhrIsvPYuOhZDWjbI5s4aftZc0u2cTyks6lx7JjCFiRfiGaOzDHNDI2WKRoeyRhBa4HqCCO8L9oCIiAiIgIiICIiAiIgKC1rpSHWenLWMfM6pYcBLUvRjeSnYaeaKdn/iY8BwHcdtjuCQZ1fiWVkMb5JHtjjYC5z3HYNA7ySgrvDbVj9c6EwecmhbWtXKzXWYG90U482Vg6no17XN/mVlXP8AgI17+EuAuSdp+ybZsqwSjZ4ZamksMDh6Dyyt3Ho7l0BAREQEREBERAREQYLNCtckgfPXinfA/tIXSMDjG7YjmaT3HYkbj0EqBrYHI6Zq1ocNcfdx9SpNG3H5OaSaWaTfmi+6nuc8AdWkvD+hB6FvnWVEFOyGY8YmmyetLj8h4KyeejOWufBz7+aXNJY4gtcCWuI6d/ULyDQ4ewYrNaz0zrLTfEHL+Oc7bswzYC9d8V3qlqTmBlEUzYYy0OLXteBuG9Obde1dQY6a+6Ls4y9oB32cBt/eoWbHXK0sDJK8ju3kMbCxhcGnlLvOI3DR5p847Dcgd5AIed59GZCsfdE1YMRddWyGNgr40GB7vDA3ENi5YiR9tIcOXpuebp3qM0O/McKdcQZnM6U1Bmauc0ph6de1jMe+zLQmrxvE1aVg86Lmc9rt3bN3B3O46eovEl38Af6w+lPEl38Af6w+lByb3K+Iv4LghomhlKFnGX4YCJalyJ0UsR7V52c1wBB2IXoZVavhrjJ4nOh2AcCTzD4f41aUBERAREQEREBERAREQV3B0/BdXamkFC3B4Sa0xtzTc8M57Pk2jb95y8g5h6S4H0qxKvXaD6mtcfkq+Nmsm3XfRt22WuVleNm8kZdEffbuL2hzeo5/SCSLCgIiICIiAiIgKvaz1czSePg7Gscll7svg2OxrH8jrU5BIbzbHlY0Bznv2PKxrjsdtjval1HR0lhLWVyUpiqQBu4Y0vfI9zg1kbGDq+R73NY1jQS5zmtAJICgtG6evzZCXVGo4msz9qIwwVA5r24uqS13g7HDoXOLWOleCQ9zWgEtjj2De0XpLyYqWZrVjxhnMjL4TksgW7dtLtsGtH3sTBsxjPQ0DcucXOdYkRAREQEREEBXbLpvIx1Wx2bWMuShlaOvVjEWNDYwOQ8mx7NxbuCWu5XOcC4NLQ2fWG5Tr5GpPUtwR2qs8bopYJmB7JGOGzmuaehBBIIPeoWhaOnbVfE3ZmeDTOEOLeBM95YyMbxzSPLg6ToSHOcC8b9N2kkLAi0MVnsZnfDPFuRqZDwOy+nZ8FnbL2E7NueJ/KTyvbuN2nqNxuFvoCIiAiIgIiICIiAqBxasy5ijT0RRmkhyWp+0ryyQHZ9bHs5fDJ9wQW7Me2Jrh1EliI/DtcM9naOmcPaymSnFalWZzySEEnv2DQB1c4kgBo3JJAAJIVc0DgbzrF7VOdgNfP5hrPuN7w/xbVaPtVUEdNwS58hBO8kj9iWtZsFur14qkEcEMbYYY2hjI2DZrWgbAADuACyIiAiIgIiICIiAiIgIiICxW6kF+rNWswx2K0zHRywytDmSMI2LXA9CCCQQVlRBXtPWTjMjZ0/YnpB1djZMdBFPI+w6mGtaHSiTc8wk5m8wc7ccpOxdsLCq5n5xS1XpeTwuhWNmWxS7OxFvPY3hdN2cL+8EdhzuHcWxn0tCsaAiIgIiICIiAiIgIiICIiCB1viG5jTlkNxoy9yo5l+lTNk1u1tQPEsDe1HvAZGNBOxGxIIIJBmKk5tVYZjGYjIxrzG5wcW7jfYlpIO3yEhZlWdFVm4UZPBMr0KVehZL6dalMXEVpfPa57Cd4yZO2aB73aPzdh0AWZERAREQFgvXq2LpWLlyxFUp143TTWJ3hkcTGjdznOPQAAEknoAFnXPIWnizlm2XcrtDUJWvrAEObmrDSCJflrRuHm/hXtLtuzYx0oZ9OUJ9e5enq3KwTVsbX5n4LF2ozG+MEFvhkzD1bK9hIYxwDo2PIcGve9rb4iICIiAiKB13k58LovOXqr+yswU5XxSbb8juU7O29Ox67fIr0UzXVFEd6YzyfcprzTeEtPq389jadlm3PDNaY17d+7cE7jf5Vp/ZU0d60Yn2yP6V+Mdjq+Jpx1qsYjiZ8u5cT1LnE9XOJ3JcepJJPVbK3dFZjumf5jlJkw/ZU0d60Yn2yP6Vy73SWu8tqHhTksbwt1vp3G6lskRvs2cg2KVtctdzivIDsyYnlAefeguILXBpHV0U9FZ3TxjknJ4e/wCzpzOR4RZPXWk9aubia07or9a5YsMdWfK0lkgZKCWuLg5h6HqGn4Cvbf2VNHetGJ9sj+lZkTorO6eMcjJh+ypo71oxPtkf0p9lTR3rRifbI/pWZE6KzunjHIyYfsqaO9aMT7ZH9KfZU0d60Yn2yP6VmROis7p4xyMmH7KmjvWjE+2R/Sn2VNHetGJ9sj+lZkTorO6eMcjJh+ypo71oxPtkf0p9lTR3rRifbI/pWZE6KzunjHIyc/qcQtNa51d40ymextTT+GmezGULNhjXW7TTs65I0nflZs5sLTsOr5DzbxFl6+ypo71oxPtkf0rMidFZ3TxjkZMP2VNHetGJ9sj+lPsqaO9aMT7ZH9KzInRWd08Y5GRT4j6VyFiOCtqPFzTSODGRtuR7uce4Ab9T8isarFmtDcryQWImTwSNLXxSNDmuB7wQehCcO7ctnT0kUsj5fBLlmox8ji5xjjme1gJJJJDQBuTudtz3rxu2qIo16Mct/wD0Iy7lnREWNAiIgIiICIiAiIgrmqLHY5zSDPDKNbtcpIzsrcXPJY+4rTuzgP3sg5ecn/cZIPSrGq7qiwYc5pBguUK3a5SRhiuR80tj7itO5ID97IOXnJ/BslHpViQEREBERAREQEREBERARV3O6pnp3/F2LpMyOQawSTdtMYYYGE7DmeGuPMdiQ0A9BueUEbxflFrD8TYP+1Jvq6006NcqjHL+ZiE4LsvIvui/d2aT4C8SZ8ZS0zaz+o4afYZDtKjqO+0nNDH4TIOYsaHTPHLFIx3atLXjdy9CeUWsPxNg/wC1Jvq64z7pT3Ptj3SmnoKuVw+ExebpuBp5qvflfNE3fzo3Dwcc7DufN3Gx6g9+9+q3PDjHNODv2gtTv1robT2oZKRxz8tjq991N0naGEyxtfyc2w5tubbfYb7dw7lPLnWBt6q0/g8di6+GwfYUq0daP9k5h5rGho/+3+ALf8otYfibB/2pN9XTqtzw4xzMF2RUnyi1h+JsH/ak31dRepb2vM3h5qNKDD4iWchj7kGRldKyPcc/Zk19mvLdwHkHlJB2O2ydVueHGOZg2sxYk4m5i5p6k9zdLUnGHNXo3FvhkvpowuHoH/fPHQbiJu7zJ2N8ggjqwRwwxsihjaGMjY0Na1oGwAA7gAqBhbOotO4mrjMbp3AVKNWMRQwx5SbZrR/+PuT8JPUnqeqsOC1TPcv+LspSbjr7mGSHsZjNDOwHY8ry1p5huCWlo6HccwB2pVo1ymMcv4mJRgsSIizIEREBVbil/BzqP8hl/wAJVpVW4pfwc6j/ACGX/CVo0ftqPOPdanbDMiItaoiIgIojV2Yu4DTeQyOPxzMrcrR9oypJaZVa/qN+aV/msAG5JPoB71xCl7r6k7RmtsrbwdV+U0u6mJquIzcN+nM21J2cTxba0Na0ODufmaOQN32PRVmYjaPQyLz/AMQ+J2axY4X5rUQZoum/UUvjBtPL+E1Z6bcfZkD3SsawPjJaHcrm97Adu5dV4ZazucQtJwags4V+Dq3nulx8E83PNLUP7VNI3lHZue3zuz3dygjc77gImJyFrRcg90rqXM6XwmibWD8MluS6tx0DqdKz4O64xzn7wOcSByPIAId5vw9y0Lnujr2CwOrX5zSHi7UWm72Mq2sXHkhNDJFenjihmjnEY3A53ktLAd4yPTuGtEDtyLlXE3joOHOb1DjzhPGAxGk59Udp4X2fa9nL2fYbch5d+/n3O3+6oVvHbWk2rqOm4uG0PjLKYx2Xx3a6gjbGazHNa8TuEJ7KQGSMcrBICX++2BKa0Dt6LjFr3QluzwrwOtcTpyj4PffNFbjz2fgxcNGWKR0T43TPa4PPaMe0bDry7nbdYKXulzqHTPD3Jad0w/K3NYWrdGKnJkI4hWnrslMnNIGua5gML/Ob3t85ocSGlrQO3IqTws4jzcQqecjv4nxFmsHk5MXkKIsiwxsrWMkDo5A1vOxzJGEEtaepBHRXZTtBERSC1eGn7jZH87Xv8962lq8NP3GyP52vf570udjV5x909y2oiLmIEREBERAREQEREFd1RbNfOaQjFqhXE+UkjMdyMuln2pWnclc/eyDl5ifwbJR6VYl4o90v7unN8DuL1TS1zhlXvsx1kXa11+T3N6CSCWNrot657F/NJsSC7o17O5+49g6Sy9vP6Vw2Uv452IvXaUNmxj3Sdoasj4w50Rds3m5SS3fYb7dw7kEsiIgIiICIiAiIgIiIKFjnF2stX7+i1A0Hb0eCxHb+kn+lTShMZ+/LWH5ZB/pYVNrr1/p8qfaFp2iIi81RFoyZzHxZqDEPuwNyk8D7UdMyDtXxMc1rpA3v5QXtBPdu4LXx+qsXldQZfCVbJlyeJbC65B2T29kJWl0fnEBrtw0+9J2267KBLIiKQULkTy6x0jtt1tztJ29Hgsp2/uH9CmlC5P8Afjo/8sn/ANLMvSj9XlV7SmF9REXIQIiICq3FL+DnUf5DL/hKtKq3FL+DnUf5DL/hK0aP21HnHutTthmREWtUREQUHjjw9u8UOHVzA4+xWhtOsVrLYrwcatkQzMlMEwb17N4Zyu236HuPcqFhuFfEPE6j1lqFlXRHhWfxdKkzE7WDSgNeV4MT/tYMjXxSyefyt2dyjkcASe9oqzETmPM+G9yvkpsZpyjnY9Py4mprCXUc+nq5lfj6dY1nxtrVw9nnDtSJC1zWM852w9Bu+jbLeAGKm0vnJr1/AxWZHadfjMVeyE0FE7EV5+xgeGmJzixhLjzMDe7YrsKKNWI2DkGq5IOOQ01Hpx1us7T+o8fmbfjrE3ceHwxueXNiM0Ded5+AdB6SNxvGa/4C5nWWU4oWIcjRqDUkGEdjHv53mKxQlfN9ubsNmOfyDzSTtzdBsN+5Ip1cdo866x4K8ROI2T1hk8zPpmhPl9F2dNVKlGzYkZDM+XnD3yOiBcw9dyGgjoOV3UnocHDfJxcV9K6nM9Q0MVpuxh54w93aumklrva5o5dizaF25JB6jp37dHRNWB5tw3uedX6XqaGt1HaazWS0/LmQ+hl5JvAwLtx08diJzYiRKxhDSCzY8zgHDvMloDgFqfS0vD1t+/h7MemNQZfJzS1O1j7eG3FY5OSMtIa4SWCC0uIDW7hxPRegEUasCicONCX9H6l4gZG5NWlg1BmxkqrYHOLmRirBFtJu0AO5onHYEjYjr6Be0RWjIERFILV4afuNkfzte/z3raWrw0/cbI/na9/nvS52NXnH3T3LaiIuYgREQERUfixrGXS+Ehq0ZOzymSc6GGQbbwsA3klAPeWggDv857dwRuvazZqv3ItUbZH3WHFfGaXsPpV4n5fJs6Pr13AMhO24Ejz0ae7oN3dQdtjuqLPxs1PK8mHGYmsz0MkklmI/5hyb/wBCpEcYjbsNzuS4lxJLiTuSSepJJJJPUkr9L7qz8K0W1ThVTrTvnkY7lw+zRq34rhfm5v00+zRq34rhfm5v01T0Wj8P0T+3CNZX+K+m28Y9X6P1HqDG4l9/TNnwiARxv5LDdw4RS7k8zA4B2w29PwldQ+zRq34rhfm5v01T0T8P0T+3BrLh9mjVvxXC/Nzfpr6ONGrAetTDOHwBkw/v51z3TmosfqzCVMvirHhWPtNL4ZuRzOYbkdzgCOoPeFIqI0DQ6oxi3GBrS6dhOOhMzY87iDUjJA8LoSmdjflcwtDgP5PP/wCu3UaV6vkqkNqpPFaqzND45oXh7HtPcQR0IXmBW/hbq6TTeoYMZNITisnJ2bWOd5sFg7lrm/AHnzSB98Wnpu4nkaf8Jtxbm7o8YTHdy8UxOLu6Ii+PBERAREQUHGfvy1h+WQf6WFTahMZ+/LWH5ZB/pYVNrr1/p8qf8YWq2vNHuhcpl8/qbUdPSNvUsWV0xg23rs9LUJxePpFwlfE4xtjebMrhG4ljhycrGjdpJX6wWezXHDWulcFldR5XT+N8hqGpJocDbdRnv2rLi17jKzZ4jj5fetIHM8b7jYLr2reCui9dZ4ZnOYNl6+YW1pXdvLHHYiaSWxzxseGTNBJ2EjXAblaeW9z/AKCzWGwOLtYJxrYGHwfGSQ3bEVirFtt2bZ2SCTk2AHKXEbADbos+rOKrmmW4bVHe6i0XUsZ3UcxraQtPFk5qxFLM6G3VA5zG5ocHBxL27bP2BcDsFXddat1Fo7U/F/HYvUWUrw2c1p2lDct3H2BiI7z+WxJAJCWxAc55QAA08uw6BdzzHAnQ+exmAoW8IRBgWOjxrq9yeCWuxwAc0SRva8tdsNwSQdhvupTIcLtLZaXU8l7Dw3DqWKGHLMsOe9lpsTSyMFpOzeUHoWgHfY94BTVkebOMmYz3Bt3EDTWD1dqC9UdoeTPQz5LJSWbePtR2mQh0c7jztbI1580nbeM7bDcLp2nKuR0H7oDEYCPUWazOKzem7V6zDmLzrPLagngaJY+bpFzNmeCxgazu2aNlaaPueuH+O09nsJFgS+jnYW18k6xdsTT2Im+9YZ3yGUNHXYBwA3Oyts2kMRY1TR1HJU5szSqS0a9ntHjkhkcxz2cu/KdzGw7kEjboRuUimRMKFyf78dH/AJZP/pZlNKFyf78dH/lk/wDpZloo/V5Vf4ymF9REXIQIiICq3FL+DnUf5DL/AISrSqtxS/g51H+Qy/4StGj9tR5x7rU7YZlr36Yv1HwGWaAP23fA8seNiD0cOo7tlsItSqH8l4PjmS9vm/STyXg+OZL2+b9JTCKNWBD+S8HxzJe3zfpJ5LwfHMl7fN+kphE1YEP5LwfHMl7fN+knkvB8cyXt836SmETVgQ/kvB8cyXt836SeS8HxzJe3zfpKYRNWBD+S8HxzJe3zfpJ5LwfHMl7fN+kphE1YEP5LwfHMl7fN+knkvB8cyXt836SmETVgQ/kvB8cyXt836SeS8HxzJe3zfpKYRNWBD+S8HxzJe3zfpKSqVW067IWvkka376WQvcf4ySSVmRIiIBavDT9xsj+dr3+e9bS1eGn7jZH87Xv896tc7Grzj7p7ltREXMQIiIC4bxrle/XmPid+1x40uj3+F0p59v6jP7l3Jcv446eksUKOfgaXDG9oy0B6K79i55/kOY0n4Gl59C6/wq5Tb0umau/GOMf9CYcpRfJA4xu5CA/Y8pcNwD6NwqcMfxC3657TO35ksfW199VVNOyMXmuS85wHiDxHu6pyeGtvqWqOWtY+kfKCStDU7F/KxslQV3sk32Dnc7iXB3TlGy6z4v4h/j7TP9iWPraXuEOlcxmvHV/ExyZaQxyWJYJpYY55GbcrnxNfyvII6cwceg6rJeorvYRGMYeOHPYlzbOw5nL3+LNqfUeYoWcBWgsUYMfefHBBN4vZI4ho980vb7127epO25JUjhLuS4s6wdSv53J4SnjsHj7zK+Ismq+zNZY5z5XOb1c1nKGhvvdz1C6hJovDSv1A99Pd2fYI8ke1f9vaIuyA7/N8wbebt8Pf1UXl+Eek847GPuYnmlxtZtOtLFYlie2ADYRucx4L29PeuJHf8JVJ0e5jjE455xjOec4e8cMBD+5yHLwT0oNydqzup9P2x66OqXBpHN6Xo08To+3hcRgakQjhqXqFi1Iw7kn7Z4S3cde4jf5V98X8Q/x9pn+xLH1te9vWt0U0TTM4REd3MXNa2QlkrwMmi/bopopItu/nbI0t2+XcBamnoc3BVkGcuULtgv3Y/H1H12Buw6Fr5ZCTvv13H8StuidPSaq1dj6rGk1acsd248dzWMdzRtP8t7QNvS1r/g2Xpcu027c3K8oiE07Xo1ERflqRERAREQUHGfvy1h+WQf6WFTa08ziMji83ZymOqOycF0MFipHI1krHtHKHsL3BpBbsCCQfNBG++w0PHeZ9S8389R+srsZXIiqmY2R3xGyIjvlaYxTaKE8d5n1Lzfz1H6ynjvM+peb+eo/WVHRzvj6o5mrKbRQnjvM+peb+eo/WU8d5n1Lzfz1H6ynRzvj6o5mrKbRQnjvM+peb+eo/WU8d5n1Lzfz1H6ynRzvj6o5mrKbULk/346P/ACyf/SzL547zPqXm/nqP1lb2GxGRymbq5TI1HYyCkH+D1JJGvle9w5S95Y4tADSQACSeYk7bbGcrcTVVMbJ74nbEx3SYYLgiIuOqIiICq3FL+DnUf5DL/hKtKq3FL+DnUf5DL/hK0aP21HnHutTthmRFq5S5Nj8fPYgo2MnNG3dtSq6Nssp+Bpkexm/8pwC1qtpUnjHrm7w80NPlsdT8MvPs1qUDXRtkjZLPMyFjntMkZc3me0bNdzecDsQCv0NfZwg//DfU42/8zi+v/wDao7N4m/xVZia1/C5XSdfFZenlnDJeCTC72DzI2JvYWZOXZ7Y3Fzh970B36Vmdw1r/AB+w+Pz9nHOwmckq1c5Bp6zlo4IvBIbc3ZCMEmUPc0umY0lrHcp98B0J0sfx3MOY4iWM7hLWI0hpWyajc250L2zSsZEXs5GSukc5zpmhgbH17iQ88g2mcEd8fSrS5rtDHq+TVlp/gu3hBM0ksUG3P5vITCOfrv2PvRzdIzI+58sZnSOt9MZDUUFrD5/LOzNVrsWDLUnNoWeWUukLbEYexjeUsZ5gLSTvuK/1Ddse6Mw2Nx+enyentQ4u5hzj+2xk9eGS1KLsxhrljYpnjdzgd2OLXgD3vUbyk/GEQaix+nzo/UT89cpS5DxewVC6CBkzYuaR/hHZt5i7maOYkgEdHeao3HcDYqem8DihLhMa3H52vmrDMBg24+vaMJLo2dkJXcp5wxxeXOPmbbdRtbaGifBeJOY1bLd7d93GVMZDV7Ll8HZDJPI483MeYvdMPQNuzHf6JzETT404S9iMHkoauQdXzWel0/THZs5pJo5J2Ol25/2r7mlcD77lAPL6FFaa90HidTXMBHHp/P0qmoO3Zib9yCBsFyWJj3uiaRMXNcWxvLS9rWuA6OVRm4Kag0DprE3o9SHP1dD1cnexGIpYZzbNueSvM2MvcJXmSUdq8DkY3mLve7qS4fcBcpU05pGvqXUTbUencP4FiqdDH+CeByyVuwfNK50khlmaxz2hw5G+c48u56RjULdwQ4j5TipoiDUGS0/YwTLb3y1DK+FzLFZz3di9vJK879mGc3MG+cTygt2KmaXFLReSz3iOpq/A2s12rofFsOThfZ7Ru/Mzsw7m5hsdxtuNj8Cx8K9H5Hh/oTEacyOVrZjxXXjp1rNWiag7CONrGBzDJJu7ZvVwIB36NCyUuHOKoZ7xvFbzzrfaul7ObUOQlrczt9x4O+cxcvU7N5dh02A2CtGOECs8XOIGX0lq3h/jMNTu5J+TvWpblHHRQvnsVYashLGmZzWNHavgJcXN7tt+ux/MfuhNP5DF4Sxh8dl85kMtBYsxYenDGy1BHBJ2U7pu1kZHEI5AWEueN3dG8y3Nc8Ns9qHXOL1Rg9UVsJbx+OsY6GK1ivDGNE743SyN+2s2f9piDSdwOV24dzbClX/cnYZtjT02Pnxdt2MovozjVGFjyzbPPO6w+cAvj7OYySSuLhu08+3LsAonWxyGezxiyPELWmhcXpNmWx2ByuJdqK7lYYqfaNr87GRMLZ3HlbzOdzlrC4gN7Pfdzm2Gl7obT9yem9+Ny9TD5GCzYxmasQRtqZFkEbpZDFtIZADGxz2mRjA5rSWkr5hNEWs3xM1zlslj5MdhJ8TV03jW7tY58EZnfPIxrSeVpdO1rd9j9r32223rs/ubclldHQYDK6xZZZi8DYwGElrYrsBTjmgFd08re2PbTCIcgIMbQHO83cqP6hZa3HzH2NO4TKu01qGCTPdkMLjZIIPDMiXxGU9nGJjyBjAS50pY0d+5BBNv0JrSpr/TceYp1rNON089Z9a3ydrFLDM+GRpMbnsOz43DdrnA+gqn8VuBlHiPHph0TsXFLp8yMr1s1im5OlJE9jWOY+AvZuRyMLXBwILfTuugaew0OnsFQxsEVaGKrC2IMp1214dwOpZG3owE7nlHdurRjjmJBavDT9xsj+dr3+e9bS1eGn7jZH87Xv8APerXOxq84+6e5bURFzECIiAvjmh7S1wDmkbEHuK+og4/q3gzarTyWtMuifWcS44ud3II/khftsG/Ax3QbnZwADRSJ9L6kqu5ZdM5MOHeI2MkH9LHEL0ui71n4zpFqnVqiKvPanKdrzJ4hz/q1l/Zv+qeIc/6tZf2b/qvTaLT+O3fkj1MnmTxDn/VrL+zf9U8Q5/1ay/s3/Vem0T8du/JHqZPMniHP+rWX9m/6oNP6gcdhprL7/LW2/8AUr02ij8du/JHqZOAYThdqfNzNE1NuDq7jmnuPa+Tb08sbHHr/KLf5/T2fS2laGkMW2jQY4jfnlnlIMs7/S95AG56AdAAAAAAAAJhFy9L+IXtLyryjdAIiLmoEREBERAREQEREBERAREQEREBERAREQFVuKX8HOo/yGX/AAlWlVjibE6Xh5qNrQSfAJjsASdgwk9B1PQehaNH7ajzj3Wp2wyIvjXBwBBBB6gj0r6taoiIgIiICIiAiIgIiICIiAiIgIiIC1eGn7jZH87Xv8962lrcNmFuDuv+9kyl5zTttuPCHjf+kFRc7Grzj7p7lsREXNQIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAvjmhwIIBB6EH0r6iCoycPBD9rxmeymHqD3lSsIJI4h181naxPLW9ejQdgAAAANl+PIC/655v5mj9WVxRaus3d8cIn7LYyp3kBf8AXPN/M0fqyeQF/wBc838zR+rK4onWbnhwjkYyp3kBf9c838zR+rJ5AX/XPN/M0fqyuKJ1m54cI5GMqd5AX/XPN/M0fqyeQF/1zzfzNH6sriidZueHCORjKneQF/1zzfzNH6snkBf9c838zR+rK4onWbnhwjkYyp3kBf8AXPN/M0fqyeQF/wBc838zR+rK4onWbnhwjkYyp3kBf9c838zR+rJ5AX/XPN/M0fqyuKJ1m54cI5GMqd5AX/XPN/M0fqyeQF/1zzfzNH6sriidZueHCORjKneQF/1zzfzNH6snkBf9c838zR+rK4onWbnhwjkYyqDOH9h/m2tVZu1CffRfc0PMPSOeKFrx/G1wPwEK006cGOqQ1asLK9aFgZHFE0NaxoGwAA7gsyLzru13Mqp+3siZxERF4oEREBERAREQEREBERAREQEREBERB//Z",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from pydantic import BaseModel\n",
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "from dotenv import load_dotenv\n",
    "# Set up the state\n",
    "from langgraph.graph import MessagesState, START\n",
    "\n",
    "# Set up the tool\n",
    "# We will have one real tool - a search tool\n",
    "# We'll also have one \"fake\" tool - a \"ask_human\" tool\n",
    "# Here we define any ACTUAL tools\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.prebuilt import ToolNode\n",
    "\n",
    "\n",
    "@tool\n",
    "def search_web(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return f\"I looked up: {query}. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "\n",
    "\n",
    "tools = [search_web]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "\n",
    "# We are going \"bind\" all tools to the model\n",
    "# We have the ACTUAL tools from above, but we also need a mock tool to ask a human\n",
    "# Since `bind_tools` takes in tools but also just tool definitions,\n",
    "# We can define a tool definition for `ask_human`\n",
    "class AskHuman(BaseModel):\n",
    "    \"\"\"Ask the human a question\"\"\"\n",
    "\n",
    "    question: str\n",
    "\n",
    "\n",
    "load_dotenv()\n",
    "model = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0, streaming=True,\n",
    ").bind_tools(tools + [AskHuman])\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # If tool call is asking Human, we return that node\n",
    "    # You could also add logic here to let some system know that there's something that requires Human input\n",
    "    # For example, send a slack message, etc\n",
    "    elif last_message.tool_calls[0][\"name\"] == \"AskHuman\":\n",
    "        return \"ask_human\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# We define a fake node to ask the human\n",
    "def ask_human(state):\n",
    "    pass\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the three nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "workflow.add_node(\"ask_human\", ask_human)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Finally we pass in a mapping.\n",
    "    # The keys are strings, and the values are other nodes.\n",
    "    # END is a special node marking that the graph should finish.\n",
    "    # What will happen is we will call `should_continue`, and then the output of that\n",
    "    # will be matched against the keys in this mapping.\n",
    "    # Based on which one it matches, that node will then be called.\n",
    "    {\n",
    "        # If `tools`, then we call the tool node.\n",
    "        \"continue\": \"action\",\n",
    "        # We may ask the human\n",
    "        \"ask_human\": \"ask_human\",\n",
    "        # Otherwise we finish.\n",
    "        \"end\": END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# After we get back the human response, we go back to the agent\n",
    "workflow.add_edge(\"ask_human\", \"agent\")\n",
    "\n",
    "memory = MemorySaver()\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "# We add a breakpoint BEFORE the `ask_human` node so it never executes\n",
    "app = workflow.compile(checkpointer=memory, interrupt_before=[\"ask_human\"])\n",
    "\n",
    "display(Image(app.get_graph().draw_mermaid_png()))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:09:35.534410Z",
     "start_time": "2024-11-05T02:09:33.927348Z"
    }
   },
   "id": "9a2f05e3df05619a",
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 与agent交互\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "dc52d01205240f29"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001B[1m Human Message \u001B[0m=================================\n",
      "\n",
      "Use the search tool to ask the user where they are, then look up the weather there\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  AskHuman (call_4ba03541403b412e957ed2)\n",
      " Call ID: call_4ba03541403b412e957ed2\n",
      "  Args:\n",
      "    question: Where are you located?\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"2\"}}\n",
    "input_message = HumanMessage(\n",
    "    content=\"Use the search tool to ask the user where they are, then look up the weather there\"\n",
    ")\n",
    "for event in app.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:10:49.206875Z",
     "start_time": "2024-11-05T02:10:47.445931Z"
    }
   },
   "id": "144bab48e616354",
   "execution_count": 8
  },
  {
   "cell_type": "markdown",
   "source": [
    "现在我们更新这个线程来回应用户，并继续运行。\n",
    "\n",
    "因为我们将它视为工具调用，我们需要更新状态，就像它是工具调用的回应一样。为了做到这一点，我们需要检查状态以获取工具调用的ID。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "4ed2ff2b45444708"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "('agent',)"
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tool_call_id = app.get_state(config).values[\"messages\"][-1].tool_calls[0][\"id\"]\n",
    "\n",
    "# We now create the tool call with the id and the response we want\n",
    "tool_message = [\n",
    "    {\"tool_call_id\": tool_call_id, \"type\": \"tool\", \"content\": \"san francisco\"}\n",
    "]\n",
    "\n",
    "# # This is equivalent to the below, either one works\n",
    "# from langchain_core.messages import ToolMessage\n",
    "# tool_message = [ToolMessage(tool_call_id=tool_call_id, content=\"san francisco\")]\n",
    "\n",
    "# We now update the state\n",
    "# Notice that we are also specifying `as_node=\"ask_human\"`\n",
    "# This will apply this update as this node,\n",
    "# which will make it so that afterwards it continues as normal\n",
    "app.update_state(config, {\"messages\": tool_message}, as_node=\"ask_human\")\n",
    "# We can check the state\n",
    "# We can see that the state currently has the `agent` node next\n",
    "# This is based on how we define our graph,\n",
    "# where after the `ask_human` node goes (which we just triggered)\n",
    "# there is an edge to the `agent` node\n",
    "app.get_state(config).next"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:16:58.087476Z",
     "start_time": "2024-11-05T02:16:58.069538Z"
    }
   },
   "id": "b716f0b8e773439a",
   "execution_count": 9
  },
  {
   "cell_type": "markdown",
   "source": [
    "We can now tell the agent to continue. We can just pass in None as the input to the graph, since no additional input is needed"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "48527cd343e93a24"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=================================\u001B[1m Tool Message \u001B[0m=================================\n",
      "\n",
      "san francisco\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  search_web (call_188e7e9794aa4164bda03a)\n",
      " Call ID: call_188e7e9794aa4164bda03a\n",
      "  Args:\n",
      "    query: weather in san francisco\n",
      "=================================\u001B[1m Tool Message \u001B[0m=================================\n",
      "Name: search_web\n",
      "\n",
      "I looked up: weather in san francisco. Result: It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "\n",
      "The weather in San Francisco is sunny. However, the result I found seems to contain some playful, non-literal commentary about Geminis. Please check a reliable source for more accurate and detailed weather information.\n"
     ]
    }
   ],
   "source": [
    "for event in app.stream(None, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:17:21.766607Z",
     "start_time": "2024-11-05T02:17:15.649433Z"
    }
   },
   "id": "9cca7be37afe0208",
   "execution_count": 10
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
